﻿using System;
using System.Net;
using System.Net.Sockets;
using System.Runtime.Serialization;
using Fadd.Collections;

namespace Fadd.Net
{
    public class RemoteChannel : IRemoteChannel
    {
        private readonly GenericSubscriberList _subscribers;
        private readonly IObjectTransport<RemoteChannelPacket> _transport;

        /// <summary>
        /// Initializes a new instance of the <see cref="RemoteChannel"/> class.
        /// </summary>
        /// <param name="transport">Transport used</param>
        public RemoteChannel(IObjectTransport<RemoteChannelPacket> transport)
        {
            _transport = transport;
            _transport.ObjectReceived += OnObject;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="RemoteChannel"/> class.
        /// </summary>
        /// <param name="remoteEndpoint">End point to connect to</param>
        /// <remarks>
        /// Will attempt to connect to other side in the constructor.
        /// </remarks>
        /// <exception cref="SocketException">Failed to connect to remote end point.</exception>
        public RemoteChannel(EndPoint remoteEndpoint)
        {
            _transport = new BinaryTransport<RemoteChannelPacket>(remoteEndpoint);
            _transport.ObjectReceived += OnObject;
        }


        /// <summary>
        /// Initializes a new instance of the <see cref="RemoteChannel"/> class.
        /// </summary>
        /// <param name="connectedSocket">Socket connected to remote end point</param>
        public RemoteChannel(Socket connectedSocket)
        {
            _transport = new BinaryTransport<RemoteChannelPacket>(connectedSocket);
            _transport.ObjectReceived += OnObject;
        }


        private void OnObject(object sender, ObjectEventArgs<RemoteChannelPacket> e)
        {
            _subscribers.Invoke(e.Object);
        }

        #region IRemoteChannel Members

        /// <summary>
        /// Register handler for an object
        /// </summary>
        /// <typeparam name="T">Type of object to receive.</typeparam>
        /// <param name="consumer">Implementation to invoke when object is received.</param>
        public void Register<T>(IRemoteObjectConsumer<T> consumer)
        {
            _subscribers.Register<T>(consumer);
        }

        /// <summary>
        /// Send an object
        /// </summary>
        /// <param name="value">Object to send, must implement <see cref="ISerializable"/>.</param>
        public void Send(object value)
        {
            _transport.Send(new RemoteChannelPacket(value));
        }

        #endregion
    }

    [Serializable]
    public class RemoteChannelPacket
    {
        public RemoteChannelPacket()
        {
        }

        public RemoteChannelPacket(object value)
        {
            Object = value;
        }

        /// <summary>
        /// Gets or sets transported object
        /// </summary>
        public object Object { get; set; }
    }
}